package com.forgenet.rpc;

import com.forgenet.game.module.player.Player;
import com.forgenet.game.module.player.manager.PlayerManager;
import com.forgenet.message.PacketId;
import com.forgenet.message.base.req.ReqCreateRolePacket;
import com.forgenet.message.base.resp.RespCreateRolePacket;
import com.forgenet.message.base.resp.RespLoginRolePacket;
import com.forgenet.message.rpc.RpcGateDispatchOtherRequest;
import com.forgenet.message.rpc.RpcOtherDispatchClientRequest;
import com.sparrow.common.packet.PacketInvoker;
import com.sparrow.common.perf.PerfTimerManager;
import com.sparrow.common.thread.IoThreadPool;
import com.sparrow.common.util.JsonUtil;
import com.sparrow.common.uuid.TimeUUIDGenerator;
import com.sparrow.event.api.EventBus;
import com.sparrow.event.model.Event;
import com.sparrow.rpc.future.RpcFuture;
import com.sparrow.rpc.socket.session.RpcSession;
import com.sparrow.rpc.socket.session.RpcSessionManager;
import com.sparrow.rpc.util.RpcUtil;
import com.sparrow.socket.manager.PacketHandlerManager;
import com.sparrow.socket.tcp.session.impl.GameSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Component
public class DispatchFacade implements IDispatchFacade {

    private static Logger logger = LoggerFactory.getLogger(DispatchFacade.class);

    @Autowired
    EventBus eventBus;
    @Autowired
    IoThreadPool ioThreadPool;
    @Autowired
    PerfTimerManager perfTimerRegistry;
    @Autowired
    private RpcSessionManager rpcSessionManager;
    @Autowired
    private PlayerManager playerManager;
    @Autowired
    private TimeUUIDGenerator uuidGenerator;
    @Autowired
    private PacketHandlerManager handlerManager;

    @Override
    public RpcFuture<Void> dispatchToOtherReq(RpcGateDispatchOtherRequest request) {
        logger.info("收到网关转发请求: {}", JsonUtil.toJSON(request));

        RpcSession rpcSession = rpcSessionManager.getSession(request.getGateId());
        if (rpcSession == null) {
            logger.error("网关转发失败，rpcSession为空 gateId={}", request.getGateId());
            return null;
        }

        int msgId = request.getMsgId();
        Object message = request.getMsg();
        long playerId = request.getPlayerId();
        switch (msgId) {
            case PacketId.Game.REQ_CREATE_ROLE_PACKET: {
                handleCreateRole(request, message);
                break;
            }
            case PacketId.Game.REQ_LOGIN_ROLE_PACKET:  {
                handleLoginRole(request, playerId);
                break;
            }
            default : {
                handleOtherGameMessage(request, message);
                break;
            }
        }
        return RpcFuture.of(null);
    }

    private void handleCreateRole(RpcGateDispatchOtherRequest request, Object message) {
        if (!(message instanceof ReqCreateRolePacket)) {
            logger.error("创建角色消息格式错误: {}", message);
            return;
        }
        ReqCreateRolePacket req = (ReqCreateRolePacket) message;

        if (playerManager.getPlayerIdByAccount(req.getAccount()) != null) {
            logger.warn("账号:{} 已存在，不允许重复注册", req.getAccount());
            return;
        }

        Player player = new Player();
        player.setAccount(req.getAccount());
        player.setName(req.getRoleName());
        player.setPlayerId(uuidGenerator.nextId());
        player.setGateId(request.getGateId());
        playerManager.insert(player);
        playerManager.register(player);
        logger.info("创建新玩家: {}", JsonUtil.toJSON(player));

        RespCreateRolePacket resp = new RespCreateRolePacket();
        resp.setAccount(req.getAccount());
        resp.setPlayerId(player.getPlayerId());

        sendPlayerMsg(player.getPlayerId(), request.getGateId(), PacketId.Game.RESP_CREATE_ROLE_PACKET, resp);
    }

    private void handleLoginRole(RpcGateDispatchOtherRequest request, long playerId) {
        Player player = playerManager.get(playerId);
        if (player == null) {
            logger.warn("登录失败，未找到player playerId={}", playerId);
            return;
        }

        RespLoginRolePacket resp = new RespLoginRolePacket();
        resp.setPlayerId(playerId);

        sendPlayerMsg(player.getPlayerId(), request.getGateId(), PacketId.Game.RESP_LOGIN_ROLE_PACKET, resp);
    }

    private void handleOtherGameMessage(RpcGateDispatchOtherRequest request, Object msgPacket) {
        int msgId = request.getMsgId();
        logger.info("收到游戏消息 msgId={}, playerId={}", msgId, request.getPlayerId());
        //获得玩家会话
        // 可考虑使用 MethodInvoker 执行注解绑定的方法
        PacketInvoker invoker = handlerManager.getInvoker(msgId);
        Player player = playerManager.get(request.getPlayerId());
        Object[] args = buildArgs(player, invoker.getMethod(), msgPacket);
        eventBus.publish(Event.newBuilder(msgPacket).directRun(() -> {
            perfTimerRegistry.timer("request", "packetId", String.valueOf(msgId)).record(()-> {
                invoker.invoke(args);
            });
        }).build());
    }

    public static void sendPlayerMsg(long playerId, String gateId, int msgId, Object msg) {
        RpcOtherDispatchClientRequest dispatchClientRequest = new RpcOtherDispatchClientRequest();
        dispatchClientRequest.setMsgId(msgId);
        dispatchClientRequest.setMsg(msg);
        dispatchClientRequest.setPlayerId(playerId);

        RpcUtil.runAsyncRpcMethod(gateId, IDispatchFacade.class, service ->
                service.dispatchToClientReq(dispatchClientRequest)
        );
    }


    @Override
    public RpcFuture<Void> dispatchToClientReq(RpcOtherDispatchClientRequest request) {
        return null;
    }

    private Object[] buildArgs(Player player, Method method, Object msgPacket) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] args = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> parameterType = parameterTypes[i];
            if (parameterType.equals(Player.class)) {
                args[i] = player;
            } else {
                args[i] = msgPacket;
            }
        }
        return args;
    }
}
